#include "Timer.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <shared_mutex>
#include "MessageLoop.h"

namespace zeroplus::mq
{

    // TimerImpl
    struct TimerImpl
    {
        std::atomic<uint64_t> start_time{0};
        std::atomic<uint32_t> interval{0};
        std::atomic<int32_t> loop_count{Timer::kInfinite};
        std::atomic<int32_t> remain_loop_count{Timer::kInfinite};
        std::atomic<uint64_t> invoke_count{0};
        std::atomic<uint16_t> priority{Timer::kTimerPriority};
        bool is_once_type{false};
        Timer::Callback callback{nullptr};
        std::atomic<MessageLoop *> message_loop{nullptr};
        std::shared_mutex mtx;
    };

    // Timer
    Timer::Timer() : impl_(std::make_unique<TimerImpl>()) {}

    Timer::Timer(MessageLoop *message_loop) : impl_(std::make_unique<TimerImpl>()) { attach(message_loop); }

    Timer::Timer(MessageLoop *message_loop, uint32_t interval_ms, int32_t loop_count, const Callback &callback)
        : impl_(std::make_unique<TimerImpl>())
    {
        impl_->interval = interval_ms;
        impl_->loop_count = loop_count;
        impl_->remain_loop_count = loop_count;
        impl_->callback = callback;
        attach(message_loop);
    }

    Timer::Timer(uint32_t interval_ms, int32_t loop_count, const Callback &callback)
        : impl_(std::make_unique<TimerImpl>())
    {
        impl_->interval = interval_ms;
        impl_->loop_count = loop_count;
        impl_->remain_loop_count = loop_count;
        impl_->callback = callback;
    }

    Timer::~Timer() { detach(); }

    void Timer::call_once(MessageLoop *message_loop, uint32_t interval_ms, const Callback &callback)
    {
        auto *timer = new Timer(interval_ms, 1, callback);
        timer->impl_->is_once_type = true;
        timer->attach(message_loop);
        timer->start();
        // Destory by MessageLoop
        return; // NOLINT
    }

    bool Timer::is_active() const { return impl_->start_time != 0; }

    uint32_t Timer::get_interval() const { return impl_->interval; }

    int32_t Timer::get_loop_count() const { return impl_->loop_count; }

    int32_t Timer::get_remain_loop_count() const { return impl_->remain_loop_count; }

    uint64_t Timer::get_invoke_count() const { return impl_->invoke_count; }

    uint16_t Timer::get_priority() const { return impl_->priority; }

    MessageLoop *Timer::get_message_loop() const { return impl_->message_loop.load(); }

    bool Timer::attach(MessageLoop *message_loop)
    {
        if (!message_loop)
        {
            LOG_FATAL("MessageLoop is null.");
            return false;
        }
        impl_->message_loop.store(message_loop);
        return message_loop->add_timer(this);
    }

    bool Timer::detach()
    {
        if (impl_->is_once_type)
        {
            return true;
        }
        MessageLoop *message_loop = impl_->message_loop.load();
        if (message_loop)
        {
            stop();
            impl_->message_loop.store(nullptr);
            return message_loop->remove_timer(this);
        }
        return false;
    }

    void Timer::start(const Callback &callback)
    {
        if (callback)
        {
            std::lock_guard lock(impl_->mtx);
            impl_->callback = callback;
        }
        if (!is_active() && impl_->remain_loop_count != 0)
        {
            force_to_start();
        }
    }

    void Timer::restart()
    {
        impl_->remain_loop_count = impl_->loop_count.load();
        force_to_start();
    }

    void Timer::stop()
    {
        impl_->start_time = 0;
        impl_->invoke_count = 0;
    }

    void Timer::set_interval(uint32_t interval_ms)
    {
        if (impl_->interval == interval_ms)
        {
            return;
        }
        impl_->interval = interval_ms;
        uint64_t interval_nano = interval_ms == 0 ? kMinInterval : interval_ms * 1000'000;
        if (is_active())
        {
            impl_->invoke_count =
                static_cast<uint64_t>(MessageLoop::get_current_nano_time() - impl_->start_time) / interval_nano;
            MessageLoop *message_loop = impl_->message_loop.load();
            if (message_loop)
            {
                message_loop->wakeup();
            }
        }
    }

    void Timer::set_loop_count(int32_t loop_count)
    {
        if (impl_->loop_count == loop_count)
        {
            return;
        }
        impl_->loop_count = loop_count;
        impl_->remain_loop_count = loop_count;
        if (is_active())
        {
            MessageLoop *message_loop = impl_->message_loop.load();
            if (message_loop)
            {
                message_loop->wakeup();
            }
        }
    }

    void Timer::set_callback(const Callback &callback)
    {
        std::lock_guard lock(impl_->mtx);
        impl_->callback = callback;
    }

    void Timer::clear() { impl_->message_loop.store(nullptr); }

    void Timer::force_to_start()
    {
        if (!get_callback())
        {
            LOG_ERROR("Callback not set.");
            return;
        }
        impl_->start_time = MessageLoop::get_current_nano_time();
        impl_->invoke_count = 0;
        MessageLoop *message_loop = impl_->message_loop.load();
        if (message_loop)
        {
            message_loop->wakeup();
        }
        else
        {
            LOG_ERROR("Not attach message loop.");
        }
    }

    void Timer::sub_remain_loop_count() const
    {
        if (impl_->remain_loop_count <= 0)
        {
            return;
        }
        --impl_->remain_loop_count;
    }

    void Timer::set_invoke_count(uint64_t invoke_count) const { impl_->invoke_count = invoke_count; }

    void Timer::set_priority(uint16_t priority) { impl_->priority = priority; }

    uint64_t Timer::get_start_time() const { return impl_->start_time; }

    bool Timer::is_once_type() const { return impl_->is_once_type; }

    const Timer::Callback &Timer::get_callback() const
    {
        std::shared_lock lock(impl_->mtx);
        return impl_->callback;
    }

} // namespace zeroplus::mq
